local state = {}
state.name = "IDLE"
state.id = 1

if(SERVER) then

  state.lastRegroupingTime = nil
  state.regroupingInterval = 5

  function state.New()
    return table.Copy(state)
  end
  
  function state:Run(ag3)
    self:UseDefaultPhysics(ag3)    
    
    if(ag3:IsArmed()) then
      if(ag3:IsGrouped()) then
        local group = ag3:GetGroup()
        
        if(self:IsGroupReady(group)) then
          if(group.target ~= nil) then
            return ag3:SetNextState(ag3.States.AimingIndirect)
          else
            local nearestTarget = ag3:GetNearestTarget()
            
            if(ValidEntity(nearestTarget)) then
              group.target = nearestTarget:GetPos()
            
              return ag3:SetNextState(ag3.States.AimingIndirect)
            end
          end
        end
      else
        if(ag3:GetTarget() ~= nil) then
          return ag3:SetNextState(ag3.States.AimingDirect)
        else
          local nearestTarget = ag3:GetNearestTarget()
          
          if(ValidEntity(nearestTarget)) then
            local nextState = ag3:SetNextState(ag3.States.AimingDirect)
            nextState.targetEnt = nearestTarget
            
            return nextState
          end
        end
      end
      
      -- If this AG-3 is remaining idle, consider regrouping
      if(self.lastRegroupingTime == nil || CurTime() - self.lastRegroupingTime >= self.regroupingInterval) then
        self:Regroup(ag3)
      end
    end
    
    self.lastRunTime = CurTime()
    
    return self
  end
  
  function state:IsGroupReady(group)    
    -- Check if group is also ready to aim and fire
    for _, groupedAg3 in ipairs(group) do
      if(groupedAg3:IsIdle() == false) then
        return false
      end
    end
    
    return true
  end
  
  function state:UseDefaultPhysics(ag3)
    if(ag3.PhysicsSimulate == nil) then
      return
    end
    
    ag3.PhysicsSimulate = nil
    ag3:StopMotionController()
    
    local physics = ag3:GetPhysicsObject()
    
    if(physics ~= nil && physics:IsValid()) then
      physics:EnableGravity(false)
      physics:EnableDrag(true)
      physics:Wake()
    end
  end

  function state:Regroup(ag3)
    ag3:Ungroup()
  
    for _, otherAg3 in pairs(ents.FindByClass(ag3:GetClass())) do
      if(otherAg3 ~= ag3 && otherAg3:IsArmed() && otherAg3:IsIdle()) then
        local distance = otherAg3:GetPos():Distance(ag3:GetPos())
        local isInGroupingRange = distance <= ag3:GetGroupingRange() && distance <= otherAg3:GetGroupingRange()
        local isCompatible = ag3:GetTarget() == otherAg3:GetTarget() && ag3:GetEnergyPerShot() == otherAg3:GetEnergyPerShot()
        
        if(isCompatible && isInGroupingRange) then
          local group = otherAg3:GetGroup() or { otherAg3 }
          local groupSize = #group
            
          if(groupSize < ag3:GetMaxGroupSize() && groupSize < otherAg3:GetMaxGroupSize()) then
            ag3:GroupWith(otherAg3)
            group.target = ag3:GetTarget()
            
            DebugMsg(tostring(ag3) .. " grouped with " .. tostring(otherAg3) .. ".\n")
          end
        elseif(isInGroupingRange == false) then
          DebugMsg(tostring(ag3) .. " is out of range to group with " .. tostring(otherAg3) .. ".\n")
        elseif(isCompatible == false) then
          DebugMsg(tostring(ag3) .. " is incompatible to group with " .. tostring(otherAg3) .. ".\n")
        end
      end
    end
    
    self.lastRegroupingTime = CurTime()
  end
  
end

ENT.States.Idle = state
